/**
 * Copyright (C), 2019-2023,
 * FileName: MessageService
 * Author:   fanxquer
 * Date:     2023/7/10 14:22
 * Description: 站内信
 * History:
 * <author>          <time>          <version>          <desc>
 * 邱富康           2023/7/10           1.0
 */
package com.hsteach.service.third;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.hsteach.common.utils.DateUtils;
import com.hsteach.common.utils.DistinctKeyUtil;
import com.hsteach.common.utils.StringUtils;
import com.hsteach.dao.domain.calendar.CalendarHoliday;
import com.hsteach.enums.HolidayTypeEnum;
import com.hsteach.framework.feign.ClientConstant;
import com.hsteach.framework.feign.client.holiday.HolidayClient;
import com.hsteach.framework.feign.client.holiday.vo.Holiday;
import com.hsteach.framework.feign.client.holiday.vo.HolidayType;
import com.hsteach.framework.feign.client.holiday.vo.TXHolidayResp;
import org.redisson.api.RBucket;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.text.ParseException;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 〈站内信〉
 *
 * @author Qiu Fukang
 * @create 2023/7/10
 * @since 1.0.0
 */
@Service
public class HolidayService {

    @Autowired
    private HolidayClient holidayClient;

    @Autowired
    private RedissonClient redissonClient;

    public Map<Date, HolidayType> getPubHolidayTypeMap(String year){
        List<Holiday> list = getHolidays(year);
        if (CollectionUtils.isEmpty(list)) {
            return Maps.newHashMap();
        }
        Map<Date, HolidayType> ret = list.stream().flatMap(e -> {

            List<HolidayType> vacations = Lists.newArrayList();
            if (StringUtils.isNotEmpty(e.getVacation().trim())) {
                String[] split = e.getVacation().split("\\|");
                List<HolidayType> vacation = Arrays.stream(split).map(j -> {
                    HolidayType type = new HolidayType();
                    type.setType(HolidayTypeEnum.HOLIDAY.getCode());
                    type.setName(e.getName());
                    type.setDay(DateUtils.parseDate(j));
                    return type;
                }).collect(Collectors.toList());
                vacations.addAll(vacation);
            }

            if (StringUtils.isNotEmpty(e.getVacation().trim())) {
                String[] remarks = e.getRemark().split("\\|");
                List<HolidayType> remark = Arrays.stream(remarks).map(j -> {
                    HolidayType type = new HolidayType();
                    type.setType(HolidayTypeEnum.WORK_WEEKEND.getCode());
                    type.setName(e.getName());
                    type.setDay(DateUtils.parseDate(j));
                    return type;
                }).collect(Collectors.toList());
                vacations.addAll(remark);
            }
            return vacations.stream();
        }).collect(Collectors.toMap(HolidayType::getDay, e -> e, (v1, v2) -> v2));

        return ret;
    }

    /**
     * 获取节假日，自定义配置优先
     * @param year
     * @param config
     * @return
     */
    public Map<Date, HolidayType> getPubHolidayTypeMap(String year, Map<String, CalendarHoliday> config){
        List<Holiday> list = getHolidays(year);
        if (CollectionUtils.isEmpty(list)) {
            return Maps.newHashMap();
        }
        Map<Date, HolidayType> ret = list.stream().flatMap(e -> {
            List<HolidayType> vacations = Lists.newArrayList();
            Date hDate = null;
            try {
                hDate = DateUtils.parseDate(e.getHoliday(), "MM月dd号");
            } catch (ParseException ex) {
                throw new RuntimeException(ex);
            }
            Calendar calendar = Calendar.getInstance();
            calendar.setTime(hDate);
            calendar.set(Calendar.YEAR, Integer.valueOf(year));
            Date d = calendar.getTime();
            if (config.containsKey(e.getName())) {
                CalendarHoliday calendarHoliday = config.get(e.getName());
                List<String> allDay = getAllDayStr(calendarHoliday.getHolidayBegin(), calendarHoliday.getHolidayEnd());
                String[] split = new String[0];
                if (StringUtils.isNotEmpty(e.getVacation().trim())) {
                    split = e.getVacation().split("\\|");
                }
                String[] remarks = new String[0];
                if (StringUtils.isNotEmpty(e.getRemark().trim())) {
                    remarks = e.getRemark().split("\\|");
                }
                Set<String> set = new HashSet<>(Arrays.asList(split));
                set.addAll(new HashSet<>(Arrays.asList(remarks)));
                set.addAll(new HashSet<>(allDay));
                vacations = set.stream().map(j -> {
                    if (allDay.contains(j)) {
                        HolidayType type = new HolidayType();
                        Date date = DateUtils.parseDate(j);
                        if (d.compareTo(date) == 0) {
                            type.setType(HolidayTypeEnum.CUR_HOLIDAY.getCode());
                        } else {
                            type.setType(HolidayTypeEnum.HOLIDAY.getCode());
                        }
                        type.setName(e.getName());
                        type.setDay(date);
                        return type;
                    } else {
                        HolidayType type = new HolidayType();
                        type.setType(HolidayTypeEnum.WORK_WEEKEND.getCode());
                        type.setName(e.getName());
                        type.setDay(DateUtils.parseDate(j));
                        return type;
                    }
                }).collect(Collectors.toList());
            } else {
                if (StringUtils.isNotEmpty(e.getVacation().trim())) {
                    String[] split = e.getVacation().split("\\|");
                    List<HolidayType> vacation = Arrays.stream(split).map(j -> {
                        Date date = DateUtils.parseDate(j);
                        HolidayType type = new HolidayType();
                        if (d.compareTo(date) == 0) {
                            type.setType(HolidayTypeEnum.CUR_HOLIDAY.getCode());
                        } else {
                            type.setType(HolidayTypeEnum.HOLIDAY.getCode());
                        }
                        type.setName(e.getName());
                        type.setDay(date);
                        return type;
                    }).collect(Collectors.toList());
                    vacations.addAll(vacation);
                }
                if (StringUtils.isNotEmpty(e.getRemark().trim())) {
                    String[] remarks = e.getRemark().split("\\|");
                    List<HolidayType> remark = Arrays.stream(remarks).map(j -> {
                        HolidayType type = new HolidayType();
                        type.setType(HolidayTypeEnum.WORK_WEEKEND.getCode());
                        type.setName(e.getName());
                        type.setDay(DateUtils.parseDate(j));
                        return type;
                    }).collect(Collectors.toList());
                    vacations.addAll(remark);
                }
            }

            return vacations.stream();
        }).collect(Collectors.toMap(HolidayType::getDay, e -> e, (v1, v2) -> v2));

        return ret;
    }

    public List<Holiday> getHolidays(String year) {
        RBucket<TXHolidayResp> holidayRespRBucket = redissonClient.getBucket("holiday-" + year);
        TXHolidayResp resp;
        if (holidayRespRBucket.isExists()) {
            resp = holidayRespRBucket.get();
        } else {
            resp = holidayClient.getTXHoliday(year);
            if (!ClientConstant.AUTH_SUCCESS.equals(resp.getCode())) {
                return Lists.newArrayList();
            }
            holidayRespRBucket.set(resp, 24, TimeUnit.HOURS);
        }

        if (CollectionUtils.isEmpty(resp.getResult().getList())) {
            return Lists.newArrayList();
        }
        return resp.getResult().getList();
    }

    public List<Holiday> getHolidays(Date begin, Date end) {
        List<String> allYear = getAllYear(begin, end);
        if (CollectionUtils.isEmpty(allYear)) {
            return Lists.newArrayList();
        }
        List<Holiday> ret = Lists.newArrayList();
        for (String s: allYear) {
            List<Holiday> holidays = getHolidays(s);
            holidays.stream().forEach(e -> {
                String s1 = e.getHoliday().replaceAll("月", "-").replaceAll("号", "");
                e.setHoliday(s + "-" + s1);
            });
            ret.addAll(holidays);
        }

        List<Holiday> collect = ret.stream().filter(DistinctKeyUtil.distinctByKey(Holiday::getName)).collect(Collectors.toList());

        return collect;
    }

    public Map<Date, HolidayType> getHolidayDateMap(Date begin, Date end) {
        List<String> allYear = getAllYear(begin, end);
        if (CollectionUtils.isEmpty(allYear)) {
            return Maps.newHashMap();
        }
        Map<Date, HolidayType> ret = Maps.newHashMap();
        for (String s: allYear) {
            Map<Date, HolidayType> pubHolidayTypeMap = getPubHolidayTypeMap(s);
            pubHolidayTypeMap.entrySet().stream().forEach(e -> {
                if (e.getKey().getTime() >= begin.getTime() && e.getKey().getTime() <= end.getTime()) {
                    ret.put(e.getKey(), e.getValue());
                }
            });
        }

        return ret;
    }

    public Map<Date, HolidayType> getHolidayDateMap(Date begin, Date end, List<CalendarHoliday> calendarHolidays) {
        List<String> allYear = getAllYear(begin, end);
        if (CollectionUtils.isEmpty(allYear)) {
            return Maps.newHashMap();
        }
        Map<String, CalendarHoliday> config = calendarHolidays.stream().collect(Collectors.toMap(CalendarHoliday::getHolidayName, e -> e, (v1, v2) -> v2));
        Map<Date, HolidayType> ret = Maps.newHashMap();
        for (String s: allYear) {
            Map<Date, HolidayType> pubHolidayTypeMap = getPubHolidayTypeMap(s, config);
            pubHolidayTypeMap.entrySet().stream().forEach(e -> {
                if (e.getKey().getTime() >= begin.getTime() && e.getKey().getTime() <= end.getTime()) {
                    ret.put(e.getKey(), e.getValue());
                }
            });
        }
        List<CalendarHoliday> holidays = calendarHolidays.stream().filter(e -> {if (e.getHolidayName().equals("寒假") || e.getHolidayName().equals("暑假")){return true;}return false;}).collect(Collectors.toList());
        if (holidays.size()>0){
            for (CalendarHoliday h :holidays){
                java.util.Calendar beginCalendar = java.util.Calendar.getInstance();
                beginCalendar.setTime(h.getHolidayBegin());
                while (!beginCalendar.getTime().after(h.getHolidayEnd())) {
                    HolidayType holidayType = new HolidayType();
                    holidayType.setType(HolidayTypeEnum.HOLIDAY.getCode());
                    holidayType.setName(h.getHolidayName());
                    holidayType.setDay(beginCalendar.getTime());
                    ret.put(beginCalendar.getTime(), holidayType);
                    beginCalendar.add(java.util.Calendar.DATE, 1);
                }
            }
        }
        return ret;
    }

    private List<String> getAllYear(Date begin, Date end) {
        Integer beginYear = Integer.valueOf(DateUtils.parseDateToStr("yyyy", begin));
        Integer endYear = Integer.valueOf(DateUtils.parseDateToStr("yyyy", end));
        List<String> dates = Lists.newArrayList();
        while (beginYear <= endYear) {
            dates.add(beginYear.toString());
            beginYear++;
        }
        return dates;
    }

    public List<String> getAllDayStr(Date begin, Date end) {
        java.util.Calendar beginCalendar = java.util.Calendar.getInstance();
        beginCalendar.setTime(begin);
        java.util.Calendar endCalendar = java.util.Calendar.getInstance();
        endCalendar.setTime(end);
        List<String> dates = Lists.newArrayList();
        while (!beginCalendar.after(endCalendar)) {
            dates.add(DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD, beginCalendar.getTime()));
            beginCalendar.add(java.util.Calendar.DATE, 1);
        }
        return dates;
    }



}
